/*
-------------------------------------------------------------------
BIE is Copyright 2001-2004 Brunswick Corp.
-------------------------------------------------------------------
Please read the legal notices (docs/legal.txt) and the license
(docs/bie_license.txt) that came with this distribution before using
this software.
-------------------------------------------------------------------
 */
/*
 * RandomLoad.java
 *
 * Created on December 11, 2002, 10:50 AM
 */

package com.webdeninteractive.bie.qa.load;

import com.webdeninteractive.toolkit.threadpool.*;
import java.util.*;
import com.webdeninteractive.toolkit.*;
import java.io.*;
import java.util.regex.*;

/**
 *
 * @author  jd
 */
public class Load {
/*
Benchmarking scorpion.wdi.com (be patient).....done
Server Software:        Apache
Server Hostname:        scorpion.wdi.com
Server Port:            5803
 
Document Path:          /services/BIERequest
Document Length:        447 bytes
 
Concurrency Level:      1
Time taken for tests:   9.344 seconds
Complete requests:      10
Failed requests:        0
Broken pipe errors:     0
Total transferred:      5880 bytes
Total POSTed:           7040
HTML transferred:       4470 bytes
Requests per second:    1.07 [#/sec] (mean)
Time per request:       934.40 [ms] (mean)
Time per request:       934.40 [ms] (mean, across all concurrent requests)
Transfer rate:          0.63 [Kbytes/sec] received
                        0.75 kb/s sent
                        1.38 kb/s total
 
Connnection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0     0    0.0      0     0
Processing:    56   934  308.1   1029  1043
Waiting:       56   927  306.5   1029  1039
Total:         56   934  308.1   1029  1043
 
Percentage of the requests served within a certain time (ms)
  50%   1029
  66%   1029
  75%   1031
  80%   1039
  90%   1043
  95%   1043
  98%   1043
  99%   1043
 100%   1043 (last request)
 */
    private static int completed=0;
    private static int failed=0;
    private static long requestTime;
    private static long testStarted;
    private static long testCompleted;
    private static long transferred;
    
    private static int numRequests;
    private static int connRequests;
    private static int wait;
    private static int numReps;
    private static long fileSize = -1;
    
    private static String reportFile;
    
    private static FileWriter fw;
    
    private static String[] services;
    private static HashMap hm = new HashMap();
    
    static PrintWriter exlog = null;
    
    public static void report(int id, long time,byte[] response,String type) {
        completed ++;
        requestTime += time;
        //transferred += response.length;
        
        DataContainer dc = (DataContainer)hm.get(type);
        dc.incCompleted();
        dc.addTime(time);
        //dc.addTransferred(response.length);
        
        String msg = "";
        if (response != null) {
            msg = new String(response);
        }
        if ( msg.length() >= 80 ) {
            msg = msg.substring( 0, 80 - 7 ) + " {...}";
        }
        System.out.println("COMPLETED " + type + " #" + id + " in " + time + " ms. Response=" + msg);
    }
    
    public static void reset() {
        System.out.println("resetting all vales");
        
        completed=0;
        failed=0;
        requestTime=0;
        testStarted=0;
        testCompleted=0;
        transferred=0;
        
        numRequests=0;
        connRequests=0;
        wait=0;
        numReps=0;
        fileSize = -1;
        
        reportFile = null;
        
        try {
           fw.close();
        }catch (Exception e) {
            //do nothing
        }    
        
        try {
           exlog.close();
        }catch (Exception e) {
            //do nothing
        }    
                      
        fw = null;
        services = null;
        hm = new HashMap();
        
        exlog = null;
        
    }
    
    
    public static void report(int id, Exception e,String type) {
        failed++;
        DataContainer dc = (DataContainer)hm.get(type);
        dc.incFailed();
        System.out.println("FAILED " +
        type + " #" + id + " " + e.getMessage());
        if ( exlog==null ) {
            e.printStackTrace();
        } else {
            synchronized (exlog) {
                exlog.println("FAILED " +
                type + " #" + id + " " + e.getMessage());
                e.printStackTrace(exlog);
            }
        }
    }
    
    private static int count = 0;
    
    private static synchronized int getCount() {
        count++;
        return count;
    }
    
    public static void runLoadTest(Properties props) throws Exception{
                
        numRequests = Integer.parseInt(props.getProperty("numRequests"));
        connRequests = Integer.parseInt(props.getProperty("numConcurrent"));
        wait = Integer.parseInt(props.getProperty("wait")) * 1000;
        numReps = Integer.parseInt(props.getProperty("repititions"));
        reportFile = props.getProperty("reportFile");
        services = StringTools.split(props.getProperty("services"),",");
        
        String exlogName = props.getProperty( "exceptionLog" );
        if ( exlogName != null ) {
            try {
                exlog = new PrintWriter(
                new BufferedWriter( new FileWriter(exlogName) ) );
                System.out.println("Exception log: " + exlogName);
            } catch (IOException e) {
                System.out.println("FAILED to create exception log: " + e);
            }
        } else {
            System.out.println("Not using exception log");
        }
        
        FileInputStream fis = null;
        for (int i=0;i<services.length;i++) {
            String messageFileName = props.getProperty( "message" );
            messageFileName =
            props.getProperty( services[i]+".message", messageFileName );
            File f = new File( messageFileName );
            if(fileSize == -1) {
                fileSize=f.length();
            }
            
            fis = new FileInputStream(f);
            byte[] msg = new byte[(int)f.length()];
            fis.read(msg);
            fis.close();
            
            HashMap map = new HashMap();
            Iterator iterator = props.keySet().iterator();
            while (iterator.hasNext()) {
                String name = (String) iterator.next();
                if (name.startsWith(services[i])) {
                    Matcher m = Pattern.compile("^"+services[i]+"\\.(.+)$").matcher(name);
                    if (m.find() && !m.group(1).equals("class") && !m.group(1).equals("message")) {
                        map.put(m.group(1),props.getProperty(name));
                    }
                }
            }
            
            DataContainer dc = new DataContainer(props.getProperty(services[i] + ".class"),msg,map);
            
            hm.put(services[i],dc);
        }
        
        ThreadPoolManager tpm = new ThreadPoolManager(connRequests,new LoadListener(),0);
        
        String service = "";
        DataContainer dc2 = null;
        
        testStarted = new Date().getTime();
        
        Runtime.getRuntime().addShutdownHook(new LoadShutdownHook());
        
        for (int k=0; k < numReps; k++) {
            System.out.println("ITERATION #" + k);
            for (int i=0;i<numRequests;i++) {
                int val = ((int)(Math.random()*5000) % services.length); //max limit is set to 9
                service = services[val];
                dc2 = (DataContainer)hm.get(service);
                tpm.putTask(new LoadTask(dc2.getClassName(), dc2.getMessage(),getCount(),services[val],dc2.getProperties()));
            }
            
            int running = 0;
            int queued = 0;
            int waiting = 0;
            int stopped = 0;
            
            while (running!=0 || queued !=0 || waiting != connRequests) {
                Thread.sleep(2000);
                running =tpm.getRunning();
                queued = tpm.getQueued();
                waiting = tpm.getWaiting();
                stopped = tpm .getStopped();
                
                System.out.println("----------------------------------------------------");
                System.out.print("Running: " + running);
                System.out.print(" Queued: " + queued);
                System.out.print(" Waiting: " + waiting);
                System.out.println(" Stopped: " + stopped);
                System.out.println("----------------------------------------------------");
                
            }
            if ( numReps-k > 1 ) {
                dumpResults();
                Thread.sleep(wait);
            } else {
                // hack to ensure the program terminates
                System.out.println("Done, exiting...");                
                //System.out.println("shutting down tpm");
                dumpResults();                
                //fw.close();                
                tpm.shutdown();
                //reset();
                //System.exit(0);
                return;
            }
        }
        
    }
    
    public static void main(String[] args) throws Exception {
        Properties props = PropertiesTools.getPropObj(System.getProperty("xbotts.home") + "/conf/load.properties");
        runLoadTest(props);
    }
    
    static void dumpResults() {
        try {
            
            testCompleted = new Date().getTime();
            
            System.out.println("\n\n----------------------------------------------------");
            System.out.println("RESULT TOTALS");
            System.out.println("----------------------------------------------------");
            
            long testTime = testCompleted - testStarted;
            
            
            if (reportFile != null) {
                fw = new FileWriter(new File(reportFile),true);
                fw.write("Request File Size,Services,Test Time,Concurrency Level,Total Requests,Completed Requests,Failed Requests,Average Request time\n");
                fw.write(fileSize + ",");
            }
            
            System.out.print("Connectors used:\t\t");
            for (int j=0;j<services.length;j++) {
                System.out.print(services[j] + ",");
                if (reportFile != null) fw.write(services[j]);
            }
            
            System.out.println("\nTotal Test Time:\t\t" + testTime + " ms" );
            if (reportFile != null) fw.write("," + testTime);
            
            System.out.println("\nConcurrency Level:\t\t" + connRequests );
            if (reportFile != null) fw.write("," + connRequests);
            
            dumpDetail(testTime,completed,failed,transferred);
            
            if (services.length > 1) {
                System.out.println("\n----------------------------------------------------");
                System.out.println("Breakdown");
                System.out.println("----------------------------------------------------");
                for (int j=0;j<services.length;j++) {
                    DataContainer dc3 = (DataContainer)hm.get(services[j]);
                    System.out.println("\nService:\t\t\t" + services[j]);
                    dumpDetail(testTime,dc3.getCompleted(),dc3.getFailed(),dc3.getTransferred());
                }
            }
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    private static void dumpDetail(long testTime,int completed,int failed,long transferred) {
        try {
            
            float requestsPer = 0;
            long aveTime = 0;
            float aveTransferred=0;
            int totalRequests = failed + completed;
            if (testTime != 0) requestsPer = ((float)completed)/((float)(testTime/1000));
            if (completed != 0) aveTime = requestTime/completed;
            if (transferred != 0 ) aveTransferred = ((float)completed)/((float)transferred);
            
            System.out.println("Total Requests:\t\t" + totalRequests);
            System.out.println("Complete Requests:\t\t" + completed);
            System.out.println("Failed Requests:\t\t" + failed);
            System.out.println("Requests per second:\t" + requestsPer);
            System.out.println("Average Request Time:\t" + aveTime + " ms");
            //System.out.println("Total Bytes Transferred:\t" + transferred);
            //System.out.println("Average Bytes Transferred:\t" + aveTransferred);
            
            if (reportFile != null) {
                fw.write("," + totalRequests + "," + completed + "," + failed + "," + requestsPer + "," +aveTime + "\n");
                fw.close();
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}

class DataContainer {
    
    private String className;
    private String destination;
    private byte[] message;
    private long transferred;
    private long time;
    private int completed;
    private int failed;
    private Map properties;
    
    public DataContainer(String className, byte[] message,Map properties) {
        this.className = className;
        this.message = message;
        this.properties = properties;
    }
    
    public String getClassName() {
        return className;
    }
    
    public Map getProperties() {
        return this.properties;
    }
    
    public byte[] getMessage() {
        return message;
    }
    
    public int getCompleted() {
        return completed;
    }
    
    public long getTime() {
        return time;
    }
    
    public long getTransferred() {
        return transferred;
    }
    public int getFailed() {
        return failed;
    }
    public void incCompleted() {
        completed++;
    }
    
    public void incFailed() {
        failed++;
    }
    
    public void addTime(long time) {
        this.time += time;
    }
    
    public void addTransferred(long transferred) {
        this.transferred += transferred;
    }
}
